#include <iostream>
#include <fstream>
#include <cstring>

#include "MapModule.h"

#define MAP_ADDRS_PTR           0xa3db
#define LOCAL_TSET_ADDR         0x175200

using namespace std;

const char* MapModule::getName() {
  return "Map Tile Data";
}

const char* MapModule::getCredits() {
  return "Written by MrTenda";
}

// Returns the address for the data of a row on the map
uint MapModule::calcMapAddress(int y) {
  return ((y / 8) * 0x100) + map_addrs[y % 8];
}

void MapModule::readFromRom(Rom &rom) {
  int i, j;
  uint addr;
  uint data;

  // Read map addrs
  data = toRegPointer(rom.readMulti(MAP_ADDRS_PTR, 3));
  for (i = 0; i < 8; i++)
    map_addrs[i] = toRegPointer(rom.readMulti(data + (i*4), 4));

  for (i=0; i < MAP_H_IN_TILES; i++) {
    rom.seek(calcMapAddress(i));
    for (j=0; j< MAP_W_IN_TILES; j++)
      tiles[i][j] = (uint) rom.read();
  }

  addr = LOCAL_TSET_ADDR;
  for (i=0; i < MAP_H_IN_TILES/8; i++)
    for (j=0; j < MAP_W_IN_TILES; j++) {
      data = rom.read(addr);
      tiles[i*8][j] |= (data & 3) << 8;
      tiles[i*8+1][j] |= ((data >> 2) & 3) << 8;
      tiles[i*8+2][j] |= ((data >> 4) & 3) << 8;
      tiles[i*8+3][j] |= ((data >> 6) & 3) << 8;
      data = rom.read(addr + 0x3000);
      tiles[i*8+4][j] |= (data & 3) << 8;
      tiles[i*8+5][j] |= ((data >> 2) & 3) << 8;
      tiles[i*8+6][j] |= ((data >> 4) & 3) << 8;
      tiles[i*8+7][j] |= ((data >> 6) & 3) << 8;
      addr++;
    }
}

void MapModule::writeToRom(Rom &rom) {
  uint c, addr;
  int i, j;

  // Read map addrs
  c = toRegPointer(rom.readMulti(MAP_ADDRS_PTR, 3));
  for (i = 0; i < 8; i++)
    map_addrs[i] = toRegPointer(rom.readMulti(c + (i*4), 4));

  for (i=0; i < MAP_H_IN_TILES; i++) {
    rom.seek(calcMapAddress(i));
    for (j=0; j < MAP_W_IN_TILES; j++)
      rom.write((uchar) (tiles[i][j] & 0xff));
  }

  addr = LOCAL_TSET_ADDR;
  for (i=0; i < MAP_H_IN_TILES/8; i++)
    for (j=0; j < MAP_W_IN_TILES; j++) {
      c = (tiles[i*8][j] >> 8)
                  | ((tiles[i*8+1][j] >> 8) << 2)
                  | ((tiles[i*8+2][j] >> 8) << 4)
                  | ((tiles[i*8+3][j] >> 8) << 6);
      rom.write(c,addr);
      c = (tiles[i*8+4][j] >> 8)
                  | ((tiles[i*8+5][j] >> 8) << 2)
                  | ((tiles[i*8+6][j] >> 8) << 4)
                  | ((tiles[i*8+7][j] >> 8) << 6);
      rom.write(c,addr + 0x3000);
      addr++;
    }
}

void MapModule::readFromDat(char* dirName, map<string, string> &fns, map<string, int> &labels) {
  int i, j;
  ifstream is;
  char buf[100];
  char c;

  getDatFname(fns, "map_tiles", "map_tiles.dat", dirName, buf);
  is.open(buf, ios::binary);
  for (i = 0; i < MAP_H_IN_TILES; i++) {
    for (j = 0; j < MAP_W_IN_TILES; j++) {
      is.get(c);
      tiles[i][j] = c & 0xff;
      is.get(c);
      tiles[i][j] += (c & 0xff) * 0x100;
    }
  }
  is.close();
}

//void MapModule::writeToDat(char* dirName) {
void MapModule::writeToDat(char* dirName, map<string, string> &fns) {
  int i, j;
  ofstream os;
  char buf[100];

  getDatFname(fns, "map_tiles", "map_tiles.dat", dirName, buf);
  os.open(buf, ios::binary);
  for (i = 0; i < MAP_H_IN_TILES; i++) {
      for (j = 0; j < MAP_W_IN_TILES; j++) {
        os.put((char) (tiles[i][j] & 0xff));
        os.put((char) (tiles[i][j] / 0x100));
      }
  }
  os.close();
}
